﻿using System;
using System.Drawing;
using System.Windows.Forms;
using Romeo.Data;

namespace Romeo.WinForms
{
    /// <summary>
    ///     A control that shows the board itself, without any decoration
    /// </summary>

    public partial class BoardDisplay : Control
    {
        #region Instance Variables

        private Board _Board = new Board ("BoardDisplay");
        private Move _LastMove = new Move ();
        private int _MouseCell = 99;
        private bool _OpponentTurn = true;
        private bool _AutoPlay = true;
        private int _ExpectedCell = 0;
        private bool _ComputerPlaysBlack;

        #endregion

        #region Constructors

        public BoardDisplay ()
        {
            InitializeComponent ();
            DoubleBuffered = true;
        }

        #endregion

        #region Runtime properties

        /// <summary>
        ///     Whether it is currently the opponent's turn.
        /// </summary>
        /// <remarks><para>
        ///     Remember that the "Opponent" is from the computer's perspective.
        ///     The opponent is therefore... the user.
        /// </para></remarks>

        public bool OpponentTurn
        {
            get
            {
                return _OpponentTurn;
            }
            set
            {
                if (_OpponentTurn != value)
                {
                    _ExpectedCell = 0;
                    _OpponentTurn = value;
                    DoBoardChanged ();
                }
            }
        }

        /// <summary>
        ///     The last move performed
        /// </summary>
        /// <remarks><para>
        ///     The last move should be consistent with the OpponentTurn property
        ///     or the display might be meaningless.
        /// </para></remarks>

        public Move LastMove
        {
            get
            {
                return _LastMove;
            }
            set
            {
                _LastMove = value;
                Invalidate ();
            }
        }

        /// <summary>
        ///     The representation of the playing board.
        /// </summary>

        public Board Board
        {
            get
            {
                return _Board;
            }
        }

        public int ExpectedCell
        {
            get
            {
                return _ExpectedCell;
            }
            set
            {
                if (_ExpectedCell != value)
                {
                    _ExpectedCell = value;
                    Invalidate ();
                }
            }
        }

        public bool ComputerPlaysBlack
        {
            get
            {
                return _ComputerPlaysBlack;
            }
            set
            {
                if (_ComputerPlaysBlack != value)
                {
                    _ComputerPlaysBlack = value;
                    Invalidate ();
                }
            }
        }

        #endregion

        #region Events

        /// <summary>
        ///     This event occurs when the user clicked on a cell that
        ///     represents a valid move.
        /// </summary>
        /// <remarks><para>
        ///     If AutoPlay is true, the board has already been updated.
        /// </para></remarks>

        public event EventHandler<CellEventArgs> ValidCellClicked;

        /// <summary>
        ///     This event occurs when the set of legal moves has
        ///     changed;
        /// </summary>
        
        public event EventHandler LegalMovesChanged;

        #endregion

        public Move PlayOn (int aCell, sbyte aPlayer)
        {
            _LastMove = _Board.PlayOn (aCell, aPlayer);
            _OpponentTurn = aPlayer == Board.Computer;
            DoBoardChanged ();
            return _LastMove;
        }

        public void SwitchSides ()
        {
            _Board.SwitchSides ();
            DoBoardChanged ();
        }

        public bool OpponentCanPass ()
        {
            return _OpponentTurn && _Board.CanPass (Board.Opponent);
        }

        protected void DoBoardChanged ()
        {
            OnBoardChanged (EventArgs.Empty);
        }

        protected virtual void OnBoardChanged (EventArgs e)
        {
            Invalidate ();
            if (LegalMovesChanged != null)
            {
                LegalMovesChanged (this, e);
            }
        }
        protected override void OnPaint (PaintEventArgs pe)
        {
            base.OnPaint (pe);
            Brush GreenBrush = new SolidBrush (Color.Green);
            Brush BlackBrush = new SolidBrush (Color.Black);
            Brush WhiteBrush = new SolidBrush (Color.White);

            Brush PlayBrush = new SolidBrush (Color.Turquoise);
            Brush FlipBrush = null;

            pe.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;

            pe.Graphics.FillRectangle (GreenBrush, pe.ClipRectangle);

            for (int i = 1; i < 8; i++)
            {
                pe.Graphics.FillRectangle (BlackBrush, FirstColPixel (i), FirstRowPixel (0), 1, Height);
                pe.Graphics.FillRectangle (BlackBrush, FirstColPixel (0), FirstRowPixel (i), Width, 1);
            }

            int smallborder = Math.Max (ColWidth (0) / 16, 2);
            int largeborder = Math.Max ((ColWidth (0) * 3) / 32, 3);
            for (int row = 0; row < 8; row++)
            {
                for (int col = 0; col < 8; col++)
                {
                    int cell = row * 10 + col + 11;
                    Brush MarkerBrush = null;

                    if (_Board._Cells[cell] == Board.Computer || _Board._Cells[cell] == Board.Opponent)
                    {
                        pe.Graphics.FillEllipse (BlackBrush, FirstColPixel (col) + smallborder, FirstRowPixel (row) + smallborder, ColWidth (col) - smallborder * 2, RowHeight (row) - smallborder * 2);
                        if (_Board._Cells[cell] == Board.Computer ^ _ComputerPlaysBlack)
                        {
                            pe.Graphics.FillEllipse (WhiteBrush, FirstColPixel (col) + largeborder, FirstRowPixel (row) + largeborder, ColWidth (col) - largeborder * 2, RowHeight (row) - largeborder * 2);
                        }

                        if (_LastMove != null)
                        {
                            if (_LastMove._Cell == cell)
                            {
                                MarkerBrush = new SolidBrush (Color.Red);
                            }
                            else if (_LastMove.HasFlip (cell))
                            {
                                if (FlipBrush == null)
                                {
                                    FlipBrush = new SolidBrush (Color.Violet);
                                }
                                MarkerBrush = FlipBrush;
                            }
                        }
                    }
                    else if (_OpponentTurn && _Board.CanPlay (cell, Board.Opponent))
                    {
                        MarkerBrush = PlayBrush;
                    }
                    else if (cell == _ExpectedCell)
                    {
                        MarkerBrush = new SolidBrush (Color.Red);
                    }

                    if (cell == _MouseCell)
                    {
                        pe.Graphics.DrawRectangle (new Pen (Color.Black), FirstColPixel (col) + 2, FirstRowPixel (row) + 2, ColWidth (col) - 2, RowHeight (row) - 2);
                    }
                    if (MarkerBrush != null)
                    {
                        pe.Graphics.FillEllipse (MarkerBrush, CenterColPixel (col) -smallborder, CenterRowPixel (row) -smallborder, smallborder*2, smallborder*2);
                    }

                }
            }
        }

        private void SetMouseCell (int aCell)
        {
            if (_MouseCell != aCell)
            {
                _MouseCell = aCell;
                Invalidate ();
            }
        }

        private void RecomputeMouseCell (MouseEventArgs e)
        {
            Point p = e.Location;
            int row, col;

            for (row = 0; row < 8; row++)
            {
                if (FirstRowPixel (row) < p.Y && p.Y < LastRowPixel (row))
                    break;
            }

            for (col = 0; col < 8; col++)
            {
                if (FirstColPixel (col) < p.X && p.X < LastColPixel (col))
                    break;
            }
            SetMouseCell (row * 10 + col + 11);
        }

        protected override void OnMouseMove (MouseEventArgs e)
        {
            base.OnMouseMove (e);
            RecomputeMouseCell (e);   
        }

        protected virtual void OnValidCellClicked (CellEventArgs e)
        {
            if (_AutoPlay)
            {
                PlayOn (e.Cell, Board.Opponent);
            }
            if (ValidCellClicked != null)
            {
                ValidCellClicked (this, e);
            }    
        }

        protected override void OnMouseClick (MouseEventArgs e)
        {
            base.OnMouseClick (e);
            RecomputeMouseCell (e);
            if (_OpponentTurn && _Board.CanPlay (_MouseCell, Board.Opponent))
            {
                OnValidCellClicked (new CellEventArgs (_Board, _MouseCell));
            }
            else
            {

            }
        }

        protected override void OnMouseLeave (EventArgs e)
        {
            base.OnMouseLeave (e);
            SetMouseCell (99);
        }

        private int FirstRowPixel (int aRow)
        {
            if (aRow == 0)
            {
                return 0;
            }
            return (Height * aRow) / 8;
        }

        private int LastRowPixel (int aRow)
        {
            return FirstRowPixel (aRow + 1) - 1;
        }
        private int CenterRowPixel (int aRow)
        {
            return (FirstRowPixel (aRow) + LastRowPixel (aRow)) / 2;
        }
        private int RowHeight (int aRow)
        {
            return LastRowPixel (aRow) - FirstRowPixel (aRow);
        }
        private int FirstColPixel (int aCol)
        {
            return (Width * aCol) / 8;
        }

        private int LastColPixel (int aCol)
        {
            return FirstColPixel (aCol + 1) - 1;
        }
        private int CenterColPixel (int aCol)
        {
            return (FirstColPixel (aCol) + LastColPixel (aCol)) / 2;
        }
        private int ColWidth (int aCol)
        {
            return LastColPixel (aCol) - FirstColPixel (aCol);
        }

        public void NewGame ()
        {
            _Board = new Board ("");
            _LastMove = null;
            _OpponentTurn = true;
            _ExpectedCell = 0;
            DoBoardChanged ();
        }
    }
}
